import chalk from 'chalk'
import { FileSpinner } from '@/utils/spinner'
import { SendConnectAction } from '../action'
import { fileStructureDiff, hashMapToFileStructure, readFileStructure } from '@/utils/fileStructure'
import type { SendConnect } from '../connect'
import type { ActionsNext } from '@/utils/action'
import type { FileStructureDiffResult } from '@/utils/fileStructure'

export class FileDiffAction extends SendConnectAction {
  constructor(connect: SendConnect) {
    super('fileDiff', connect)

    this.addAction('receiveHashMap', this.receiveHashMap_handler, true)
    this.addAsyncAction('readFile', this.readFile_handler)
    this.addAsyncAction('fileDiff', this.fileDiff_handler)
    this.addAction('hasChanged', this.hasChanged_handler)
    this.addAction('notChanged', this.notChanged_handler)
    this.addAsyncAction('needToSend', this.needToSend_handler)
    this.addAsyncAction('noNeedToSend', this.noNeedToSend_handler)

    this.actions.addBranchChain('receiveHashMap', 'readFile', 'fileDiff').addBranchList([
      ['fileDiff', 'hasChanged'],
      ['fileDiff', 'notChanged'],
      ['hasChanged', 'needToSend'],
      ['hasChanged', 'noNeedToSend']
    ])
  }

  receiveHashMap_handler(next: ActionsNext) {
    this.packetOnce((p) => {
      const { hashMap } = p.readBodyAsJson() || {}

      if (!hashMap) {
        return this.fail(new Error('receiveHashMap_handler: hashMap is null'))
      }

      const oldFileStructure = hashMapToFileStructure(hashMap)

      this.connect.store.set('oldFileStructure', oldFileStructure)

      next()
    })
  }

  async readFile_handler() {
    this.connect.startIntervalSendPong(3000)
    const fileSpinner = new FileSpinner(this.spinner, {
      prefix: 'Read'
    })
    const sensConfig = this.connect.sendInstance.currentConfig
    this.spinner.start('Start reading file...')

    try {
      const newFileStructure = await readFileStructure(sensConfig.path, {
        ignore: sensConfig.ignore,
        progress(relative, total, current) {
          fileSpinner.update({
            relative,
            totalSize: total,
            currentSize: current
          })
        },
        validator() {
          // TODO 验证读取的路径是否有和缓存路径冲突的
          return true
        }
      })

      this.connect.store.set('newFileStructure', newFileStructure)
      fileSpinner.spinner.succeed('Reading completed')
    } catch (error) {
      fileSpinner.spinner.fail('read fail: ' + String(error))
      this.fail(error as any)
    } finally {
      this.connect.stopIntervalSendPong()
      fileSpinner.end()
    }
  }

  async fileDiff_handler() {
    const oldFileStructure = this.connect.store.get('oldFileStructure')
    const newFileStructure = this.connect.store.get('newFileStructure')
    const diffResult = fileStructureDiff(oldFileStructure, newFileStructure)
    this.connect.store.set('fileStructureDiffResult', diffResult)
    await this.connect.sendJson(diffResult)
    let resultText = `File diff result ${chalk.green('A' + diffResult.added_count)} ${chalk.yellow('C' + diffResult.changed_count)} ${chalk.red('R' + diffResult.removed_count)}`
    if (diffResult.hasChanged && !diffResult.hasSended) {
      resultText += chalk.dim(' (No need to send)')
    }
    this.spinner.succeed(resultText)
    return diffResult.hasChanged ? 'hasChanged' : 'notChanged'
  }

  hasChanged_handler(next: ActionsNext) {
    const diffResult = this.connect.store.get<FileStructureDiffResult>('fileStructureDiffResult')
    next(diffResult.hasSended ? 'needToSend' : 'noNeedToSend')
  }

  notChanged_handler() {
    this.spinner.info('File not changed')
  }

  async needToSend_handler() {
    this.nextName = 'fileSend'
  }

  async noNeedToSend_handler() {
    this.nextName = 'updateFile'
  }
}
